﻿

using CSRedis;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NetCore.Cache
{
    public class RedisService : ICacheService, IDisposable
    {
        public int CacheTimeout { get => 60000; }
        private CSRedisClient _csredisCache;
        private static RedisService _instance;
        public static RedisService Instance
        {
            get
            {
                if (_instance == null)
                {
                    //new MemoryCache(Options.Create(new MemoryCacheOptions()))
                    _instance = new RedisService();
                }
                return _instance;
            }
        }
        /// <summary>
        /// 过期时间 秒
        /// </summary>
        public TimeSpan ExpireTime
        {
            get
            {
                return TimeSpan.FromMinutes(ApplicationEnvironments.Site.SessionTimeout);
            }
        }
        public RedisService()
        {
            _csredisCache = new CSRedisClient(ApplicationEnvironments.Site.RedisConnection);
            
        }
        public bool Add(string key, object value)
        {
            return this.Add(key,value,ExpireTime);
        }
        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存Value</param>
        /// <param name="expiresSliding">滑动过期时长（如果在过期时间内有操作，则以当前时间点延长过期时间,Redis中无效）</param>
        /// <param name="expiressAbsoulte">绝对过期时长</param>
        /// <returns></returns>
        public bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            return _csredisCache.Set(key, JSONHelper.ToJson(value), expiresSliding.Minutes * CacheTimeout);
        }

        public bool Add(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            return _csredisCache.Set(key, JSONHelper.ToJson(value), expiresIn.Minutes* CacheTimeout); 
        }

        public Task<bool> AddAsync(string key, object value)
        {
            return _csredisCache.SetAsync(key,JSONHelper.ToJson(value));
        }

        public Task<bool> AddAsync(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            return _csredisCache.SetAsync(key,JSONHelper.ToJson(value), expiresSliding.Minutes * CacheTimeout);
        }

        public Task<bool> AddAsync(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            return _csredisCache.SetAsync(key,JSONHelper.ToJson(value), expiresIn.Minutes * CacheTimeout);
        }

        public bool Clear()
        {
            foreach (var key in _csredisCache.Keys("*"))
            {
                _csredisCache.Del(key);
            }
            foreach (var key in _csredisCache.HKeys("*"))
            {
                _csredisCache.HKeys(key);
            }
            return true;
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }

        public bool Exists(string key)
        {
            return _csredisCache.Exists(key);
        }

        public Task<bool> ExistsAsync(string key)
        {
            return _csredisCache.ExistsAsync(key);
        }

        public byte[] Get(string key)
        {
            if (this.Exists(key))
            {
                return _csredisCache.Get<byte[]>(key);
            }
            return null;
        }

        public IDictionary<string, object> GetAll(List<string> keys)
        {
            if (keys == null)
            {
                throw new ArgumentNullException(nameof(keys));
            }
            var dict = new Dictionary<string, object>();

            keys.ToList().ForEach(item => dict.Add(item, GetCache(item)));

            return dict;
        }

        public Task<IDictionary<string, object>> GetAllAsync(List<string> keys)
        {
            throw new NotImplementedException();
        }

        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
        {
            if (this.Exists(key))
            {
                return _csredisCache.GetAsync<byte[]>(key);
            }
            return null;
        }

        public T GetCache<T>(string key) where T : class, new()
        {
            return _csredisCache.Get<T>(key);
        }

        public object GetCache(string key)
        {
            var value = _csredisCache.Get(key);
            if (!string.IsNullOrWhiteSpace(value))
            {
                return JsonConvert.DeserializeObject(value);
            }

            return null;
        }

        public Task<T> GetCacheAsync<T>(string key) where T : class, new()
        {
            return _csredisCache.GetAsync<T>(key);
        }

        public Task<T> GetCacheAsync<T>(string key, Func<T> acquire) where T : class, new()
        {
            return _csredisCache.GetAsync<T>(key);
        }

        public Task<object> GetCacheAsync(string key)
        {
            return Task.Factory.StartNew(() => { return this.GetCache(key); });
        }

        public T GetOrCreate<T>(string key, Func<T> acquire) where T : class, new()
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (this.Exists(key))
            {
                var value = _csredisCache.Get(key);
                if (string.IsNullOrWhiteSpace(value))
                {
                    return default(T);
                }
                return JsonConvert.DeserializeObject<T>(value);
            }
            var result = acquire();
            this.Add(key, result);
            return result;
        }

        public T GetOrCreate<T>(string key, Func<T> acquire, bool isSliding) where T : class, new()
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (this.Exists(key))
            {
                var value = _csredisCache.Get(key);
                if (string.IsNullOrWhiteSpace(value))
                {
                    return default(T);
                }
                return JsonConvert.DeserializeObject<T>(value);
            }
            var result = acquire();
            if (result == null)
            {
                return result;
            }
            this.Add(key, result, TimeSpan.FromMinutes(ApplicationEnvironments.Site.SessionTimeout), isSliding);
            return result;
        }

        public void Refresh(string key)
        {
            if (Exists(key))
            {
                this.Set(key, Get(key), new DistributedCacheEntryOptions()
                {
                    SlidingExpiration = this.ExpireTime
                });
            }
        }

        public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken))
        {
            return Task.Factory.StartNew(() =>
            {
                this.Refresh(key);
            });
        }

        public void Remove(string key)
        {
            _csredisCache.Del(key);
        }

        public void RemoveAll(List<string> keys)
        {
            _csredisCache.Del(keys.ToArray());
        }

        public Task RemoveAllAsync(List<string> keys)
        {
            return  _csredisCache.DelAsync(keys.ToArray());
        }

        public Task RemoveAsync(string key, CancellationToken token = default(CancellationToken))
        {
            return _csredisCache.DelAsync(key);
        }

        public bool RemoveCache(string key)
        {
           long count= _csredisCache.Del(key);
            return count > 0;
        }

        public Task<bool> RemoveCacheAsync(string key)
        {
           return Task.Factory.StartNew<bool>(() => {
               return this.RemoveCache(key);
             
            });
           
        }

        public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
        {
            if (value == null)
            {
                return;
            }
            _csredisCache.Set(key,value, options.SlidingExpiration.Value.Minutes* CacheTimeout);
        }

        public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
        {
            return _csredisCache.SetAsync(key, value, options.SlidingExpiration.Value.Minutes * CacheTimeout);

        }
    }
}

